package com.sunlands.zlcx.usercenter.controller;

import com.alibaba.fastjson.JSONObject;
import com.sunlands.zlcx.usercenter.common.annotation.AutoLog;
import com.sunlands.zlcx.usercenter.common.cache.RedisDistributedLockImpl;
import com.sunlands.zlcx.usercenter.config.RedisConfig;
import com.sunlands.zlcx.usercenter.constant.RedisKeyConst;
import com.sunlands.zlcx.usercenter.constant.YN;
import com.sunlands.zlcx.usercenter.constant.dic.LEARN_CONTENT;
import com.sunlands.zlcx.usercenter.constant.dic.LEARN_ICON_LIST;
import com.sunlands.zlcx.usercenter.constant.dic.LEARN_TIMING;
import com.sunlands.zlcx.usercenter.domain.*;
import com.sunlands.zlcx.usercenter.exception.GlobalExceptionHandler;
import com.sunlands.zlcx.usercenter.exception.UserNotLearningException;
import com.sunlands.zlcx.usercenter.service.DicServiceImpl;
import com.sunlands.zlcx.usercenter.service.UsersLearnContentServiceImpl;
import com.sunlands.zlcx.usercenter.service.UsersServiceImpl;
import com.sunlands.zlcx.usercenter.service.UsersStudyServiceImpl;
import com.sunlands.zlcx.usercenter.service.status.common.AbstractStudyStatus;
import com.sunlands.zlcx.usercenter.util.BeanPropertiesUtil;
import com.sunlands.zlcx.usercenter.util.Version;
import com.sunlands.zlcx.usercenter.vo.*;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.util.Asserts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static com.sunlands.zlcx.usercenter.config.RedisConfig.CacheKeys.USERS_BY_ID;
import static com.sunlands.zlcx.usercenter.constant.LearningStatus.NO_TIMING;
import static com.sunlands.zlcx.usercenter.constant.dic.LEARN_CONTENT.count_limit;
import static com.sunlands.zlcx.usercenter.domain.UsersStudyInfoDO.TypeEnums.ORDERNARY_COUNT_TIME;
import static com.sunlands.zlcx.usercenter.vo.CommonResultMessage.*;

/**
 * @author shixiaopeng
 */
@SuppressWarnings("unused")
@Api(tags = "9、用户学习计时内容管理")
@ApiResponses(value = {@ApiResponse(code = 400, response = GlobalExceptionHandler.class, message = "数据校验失败"), @ApiResponse(code = 500, response = GlobalExceptionHandler.class, message = "内部错误")})
@Slf4j
@RestController
@RequestMapping("/learn")
public class UsersLearnContentController implements AuthSupport, BeanPropertiesUtil {

    @Autowired
    private UsersLearnContentServiceImpl usersLearnService;
    @Autowired
    private UsersStudyServiceImpl usersStudyService;
    @Autowired
    private UsersServiceImpl usersService;
    @Autowired
    private RedisDistributedLockImpl redisDistributedLock;
    @Autowired
    protected RedisTemplate redisTemplate;
    @Autowired
    private DicServiceImpl dicService;

    @Lazy
    @Autowired
    Version version;

    @AutoLog
    @ApiOperation(value = "背景图和文案列表")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/content/background")
    public BusinessResult<List<LearnBackgroundVO>> getBackgroundList() {
        DicDO dicDO = getLearnTimeDic();
        List<DicDataDO> dicData = dicDO.getDicDataSet()
                .stream()
                .sorted(Comparator.comparing(DicDataDO::getSeq))
                .collect(Collectors.toList());
        List<String> copyList = dicData.stream().map(DicDataDO::getMark).collect(Collectors.toList());
        //默认笑脸
        DicDataDO dicDataDO = dicData.get(0);
        List<DicDataDO> subList = dicData.subList(1, dicData.size());
        Collections.shuffle(copyList);

        List<LearnBackgroundVO> resut = new ArrayList<>();
        subList.add(0, dicDataDO);
        for (int i = 0; i < subList.size(); i++) {
            LearnBackgroundVO learnBackgroundVO = LearnBackgroundVO.
                    builder().
                    backgroundUrl(subList.get(i).getData()).
                    copy(copyList.get(i)).
                    id(subList.get(i).getId()).
                    build();
            resut.add(learnBackgroundVO);
        }
        return BusinessResult.createSuccessInstance(resut);
    }

    @AutoLog
    @ApiOperation(value = "用户学习内容列表")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/content/list/personal")
    public BusinessResult<List<LearnContentVO>> getContentsByUserId(@ApiParam(value = "用户id") @RequestParam Long userId) {
        Asserts.notNull(userId, "userId buneng weikong !");
        List<LearnContentVO> learnContents = LearnContentVO.fromDO(usersLearnService.findByUserId(userId));
        List<Long> contentIds = learnContents
                .stream()
                .map(LearnContentVO::getId)
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(contentIds)) {
            return BusinessResult.createSuccessInstance(learnContents);
        }

        List<ContentCountVO> contentCount = usersStudyService.countByContentIds(contentIds);
        Map<Long, List<ContentCountVO>> listMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(contentCount)) {
            listMap = contentCount
                    .stream()
                    .collect(Collectors.groupingBy(ContentCountVO::getContentId));
        }
        Map<Long, List<ContentCountVO>> finalListMap = listMap;
        DicDO dicDOIcon = getIconDic();
        List<DicDataDO> dicDataSetIcon = dicDOIcon.getDicDataSet();
        Map<String, List<DicDataDO>> mapIcon = dicDataSetIcon
                .stream()
                .collect(Collectors.groupingBy(DicDataDO::getData));

        DicDO dicDO = getLearnTimeDic();
        List<DicDataDO> dicDataSet = dicDO.getDicDataSet();
        Map<String, List<DicDataDO>> mapBackground = dicDataSet.stream().collect(Collectors.groupingBy(o -> o.getData()));

        learnContents.forEach(learnContentVO -> {
            Long contentVOId = learnContentVO.getId();
            List<ContentCountVO> countVOS = finalListMap.get(contentVOId);
            learnContentVO.setCurDayCount(0L);
            learnContentVO.setBackgroundId(70L);
            if (CollectionUtils.isNotEmpty(countVOS)) {
                learnContentVO.setCurDayCount(countVOS.get(0).getCount());
            }
            List<DicDataDO> dataIconDOS = mapIcon.get(learnContentVO.getImageUrl());
            if (CollectionUtils.isNotEmpty(dataIconDOS)) {
                learnContentVO.setIconId(dataIconDOS.get(0).getId());
            }
            List<DicDataDO> dataBackgroundDOS = mapBackground.get(learnContentVO.getBackgroundUrl());
            if (CollectionUtils.isNotEmpty(dataBackgroundDOS)) {
                learnContentVO.setBackgroundId(dataBackgroundDOS.get(0).getId());
            }
        });

        //设置默认展示学习内容
        UsersLearnTimingDO learnTiming = usersStudyService.findLatestOnlyLearnTiming(userId);
        if (Objects.isNull(learnTiming) && !learnContents.isEmpty()) {
            learnContents.get(0).setLastUsed(YN.YES.getCode());
            return BusinessResult.createSuccessInstance(learnContents);
        }
        learnContents.forEach(c -> c.setLastUsed(Objects.nonNull(learnTiming) && c.getId().equals(learnTiming.getContentId())
                ? YN.YES.getCode()
                : YN.NO.getCode()));

        return BusinessResult.createSuccessInstance(learnContents);
    }

    @AutoLog
    @ApiOperation(value = "学习内容列表")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/content/list")
    public BusinessResult<List<LearnContentVO>> getContentList() {
        Long userId = currentUserId();
        return getContentsByUserId(userId);
    }

    @AutoLog
    @ApiOperation(value = "单个学习内容查询")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/content/one/{id}")
    public BusinessResult<LearnContentVO> getContent(@ApiParam(value = "id") @PathVariable Long id) {
        LearnContentDO contentDO = usersLearnService.findOne(id);
        if (Objects.nonNull(contentDO) && contentDO.getIsDelete().intValue() == LearnContentDO.DeleteEnum.NO.code) {
            return BusinessResult.createSuccessInstance(LearnContentVO.fromDO(contentDO));
        }
        return BusinessResult.createInstance(LEARN_CONTENT_EXIST);
    }

    @AutoLog
    @ApiOperation(value = "快速开始")
    @PreAuthorize("isAuthenticated()")
    @PostMapping("/content/fast")
    public BusinessResult<LearnContentVO> fast(HttpServletRequest request) {
        Long userId = currentUserId();
        UsersVO one = usersService.findOne(userId);
        AbstractStudyStatus.checkStatus(UsersStudyInfoDO.TypeEnums.ORDERNARY_COUNT_TIME_START.code, one);
        DicDO dicDOIcon = getIconDic();
        LearnContentVO learnContentVO = LearnContentVO
                .builder()
                .userId(userId)
                .model(LearnContentDO.ModelEnum.invalid.code)
                .imageUrl(dicDOIcon.getDicDataSet().get(0).getData())
                .content("专注计时")
                .isDelete(LearnContentDO.DeleteEnum.NO.code)
                .status(NO_TIMING.getCode())
                .build();
        return save(learnContentVO, request);
    }

    @AutoLog
    @ApiOperation(value = "保存学习内容")
    @PreAuthorize("isAuthenticated()")
    @PostMapping("/content/save")
    public BusinessResult<LearnContentVO> save(@RequestBody LearnContentVO learnContentVO, HttpServletRequest request) {

        Long userId = currentUserId();
        //校验
        Asserts.notBlank(learnContentVO.getContent(), "学习内容不能为空！");
        Asserts.notNull(learnContentVO.getModel(), "学习计时类型不能为空！");
        learnContentVO.setUserId(userId);

        //字典数据
        DicDO iconsDic = getIconDic();
        DicDO contentDic = dicService.findByCode(LEARN_CONTENT.dicName());
        DicDO LearnTimeDic = getLearnTimeDic();

        LearnContentDO contentDB = usersLearnService.findByContent(userId, learnContentVO.getContent());
        //数量限制检验

        if (Version.compareVersion(getVersion(request), "3.3.1") <= 0) {
            Long countLimit = contentDic.filter(count_limit.name()).getCode();
            Long count = usersLearnService.count(userId);
            if (count >= countLimit && (Objects.isNull(contentDB) || contentDB.getIsDelete().intValue() == LearnContentDO.DeleteEnum.YES.code)) {
                return BusinessResult.createInstance(LEARN_CONTENT_COUNT_LIMIT);
            }
        }


        //icon设置默认值
        if (Objects.isNull(learnContentVO.getIconId()) && StringUtils.isBlank(learnContentVO.getImageUrl())) {
            DicDataDO defaultIcon = iconsDic.getDicDataSet().get(0);
            learnContentVO.setIconId(defaultIcon.getId());
            learnContentVO.setImageUrl(defaultIcon.getData());
        }

        String defaultBackgroundUrl = LearnTimeDic.getDicDataSet().get(0).getData();
        learnContentVO.setBackgroundUrl(defaultBackgroundUrl);
        LearnContentDO contentDO;

        if (Objects.nonNull(contentDB) && contentDB.getIsDelete().intValue() == LearnContentDO.DeleteEnum.NO.code) {
            LearnContentVO vo = LearnContentVO.fromDO(contentDB);
            getBackgroundId(vo, LearnTimeDic);
            getIconId(vo, iconsDic);
            return BusinessResult.createInstance(LEARN_CONTENT_REPEAT, vo);
        } else {
            contentDO = usersLearnService.save(LearnContentDO.fromDO(learnContentVO));
        }
        LearnContentVO vo = LearnContentVO.fromDO(contentDO);
        getBackgroundId(vo, LearnTimeDic);
        getIconId(vo, iconsDic);
        return BusinessResult.createSuccessInstance(vo);
    }

    @AutoLog
    @ApiOperation(value = "修改学习内容")
    @PreAuthorize("isAuthenticated()")
    @PutMapping("/content/update")
    public BusinessResult<LearnContentVO> update(@RequestBody LearnContentVO learnContentVO) {
        //先校验学习内容
        LearnContentDO contentDB = usersLearnService.findByContent(learnContentVO.getUserId(), learnContentVO.getContent());
        DicDO dicDO = getLearnTimeDic();
        if (Objects.nonNull(contentDB) && contentDB.getIsDelete().intValue() == LearnContentDO.DeleteEnum.NO.code && contentDB.getId().intValue() != learnContentVO.getId()) {
            //存在，且未删除
            LearnContentVO vo = LearnContentVO.fromDO(contentDB);
            getBackgroundId(vo, dicDO);
            DicDO dicDOIcon = getIconDic();
            getIconId(vo, dicDOIcon);
            return BusinessResult.createInstance(LEARN_CONTENT_REPEAT, vo);
        }
        LearnContentDO contentDODB = usersLearnService.findOne(learnContentVO.getId());

        if (Objects.nonNull(contentDODB) && contentDODB.getIsDelete().intValue() == LearnContentDO.DeleteEnum.NO.code) {
            learnContentVO.setIsDelete(contentDODB.getIsDelete());

            boolean flag = false;
            String oldContent = contentDODB.getContent();
            String newContent = learnContentVO.getContent();
            if (StringUtils.isNotEmpty(newContent) && !contentDODB.getContent().equals(learnContentVO.getContent())) {
                //如果修改名称，把所有的删除记录都修改
                flag = true;
            }
            //只修改传了值的字段
            copyNonNullProperties(learnContentVO, contentDODB);
            LearnContentDO contentDO = usersLearnService.update(contentDODB);

            learnContentVO = LearnContentVO.fromDO(contentDO);

            getBackgroundId(learnContentVO, dicDO);
            DicDO dicDOIcon = getIconDic();
            getIconId(learnContentVO, dicDOIcon);

            if (flag) {
                usersLearnService.updateContent(currentUserId(), oldContent, newContent);
            }

            return BusinessResult.createSuccessInstance(learnContentVO);
        }
        return BusinessResult.createInstance(LEARN_CONTENT_EXIST);
    }

    private void getIconId(LearnContentVO learnContentVO, DicDO dicDOIcon) {
        List<DicDataDO> dicDataSetIcon = dicDOIcon.getDicDataSet();
        Map<String, List<DicDataDO>> mapIcon = dicDataSetIcon.stream().collect(Collectors.groupingBy(o -> o.getData()));
        List<DicDataDO> dataIconDOS = mapIcon.get(learnContentVO.getImageUrl());
        if (CollectionUtils.isNotEmpty(dataIconDOS)) {
            learnContentVO.setIconId(dataIconDOS.get(0).getId());
        }
    }

    private void getBackgroundId(LearnContentVO learnContentVO, DicDO dicDO) {
        List<DicDataDO> dicDataSet = dicDO.getDicDataSet();
        Map<String, List<DicDataDO>> mapBackground = dicDataSet.stream().collect(Collectors.groupingBy(o -> o.getData()));
        List<DicDataDO> dataBackgroundDOS = mapBackground.get(learnContentVO.getBackgroundUrl());
        if (CollectionUtils.isNotEmpty(dataBackgroundDOS)) {
            learnContentVO.setBackgroundId(dataBackgroundDOS.get(0).getId());
        }
    }

    private DicDO getLearnTimeDic() {
        return dicService.findByCode(LEARN_TIMING.dicName());
    }

    private DicDO getIconDic() {
        return dicService.findByCode(LEARN_ICON_LIST.dicName());
    }

    @AutoLog
    @ApiOperation(value = "删除学习内容")
    @PreAuthorize("isAuthenticated()")
    @DeleteMapping("/content/delete/{id}")
    public BusinessResult update(@ApiParam(value = "id") @PathVariable Long id) {
        Long userId = currentUserId();
        LearnContentDO contentDO = usersLearnService.findOne(id);
        if (Objects.nonNull(contentDO) && contentDO.getUserId().equals(userId)) {
            contentDO.setIsDelete(LearnContentDO.DeleteEnum.YES.code);
            usersLearnService.update(contentDO);
        }
        return BusinessResult.createSuccessInstance(null);
    }

    @AutoLog
    @ApiOperation(value = "针对倒计时-检查客户端是否结束计时,对应错误码15103(由于提前退出app，本次计时已自动结束！)")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/check/end")
    public BusinessResult checkEnd() {
        Long userId = currentUserId();
        //查询用户是否学习中
        LearnContentDO contentDO = usersLearnService.findLearningAndModelValid(userId);
        if (Objects.isNull(contentDO)) {
            return BusinessResult.createSuccessInstance(null);
        }
        JSONObject jsonObject = usersStudyService.getLearnTimeByUserId(userId);
        Long learnTime = jsonObject.getLong("learnTime");
        Integer learnStatus = jsonObject.getInteger("learnStatus");

        if (contentDO.getMinutes() * 60 < learnTime) {
            return BusinessResult.createInstance(LEARN_NO_END);
        }
        return BusinessResult.createSuccessInstance(null);
    }


    @AutoLog
    @ApiOperation(value = "是否保存记录--是")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/confirm")
    public BusinessResult confirm() {
        Long userId = currentUserId();
        UsersStudyInfoVO usersStudyInfoVO = UsersStudyInfoVO.builder().type(ORDERNARY_COUNT_TIME.code).userId(userId).build();
        String checkAddStudy = "";
        if (null == usersStudyInfoVO) {
            checkAddStudy = "学习VO不能为空";
        } else if (null == UsersStudyInfoDO.TypeEnums.create(usersStudyInfoVO.getType())) {
            checkAddStudy = "学习类型错误";
        }
        if (StringUtils.isNotEmpty(checkAddStudy)) {
            return BusinessResult.createInstance(PARAMS_NOT_INVALIDE, checkAddStudy);
        }
        try {
            if (redisDistributedLock.openUserLock(userId)) {
                Boolean booleanFuture = usersStudyService.addUsersStudyInfo(usersStudyInfoVO);
                // 学习计时统计结果
                if (booleanFuture && UsersStudyInfoDO.TypeEnums.create(usersStudyInfoVO.getType()) == ORDERNARY_COUNT_TIME) {
                    //如果是连麦服务，没有进行手动触发结束学习的操作，但是需要查询此次连麦的学习记录，直接返回最近一次的学习记录
                    if (Objects.isNull(usersStudyInfoVO.getStudyMinuteSeconds())) {
                        usersStudyInfoVO = UsersStudyInfoVO.from(usersStudyService.findLatestStudyInfo(userId));
                    }
                    UsersLearningResultVO learningResult = usersStudyService.getLearningResult(usersStudyInfoVO);
                    return BusinessResult.createSuccessInstance(learningResult);
                }
                return BusinessResult.createSuccessInstance(null);
            }
        } catch (UserNotLearningException ignored) {

        } finally {
            redisDistributedLock.releaseUserLock(userId);
        }
        return BusinessResult.createInstance(CommonResultMessage.UNKOWN_ERROR, "未知错误，请联系管理员");
    }


    //TODO 否

    @AutoLog
    @ApiOperation(value = "是否保存记录--否")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/cancel")
    public BusinessResult cancel() {
        Long userId = currentUserId();
        //为了兼容之前版本，清除缓存
        String pauseKey = RedisKeyConst.getPauseLearnTimeByUserid(userId);
        String continueKey = RedisKeyConst.getContinueLearnTimeByUserid(userId);
        redisTemplate.delete(pauseKey);
        redisTemplate.delete(continueKey);
        redisTemplate.opsForZSet().remove(RedisKeyConst.getUserSetLearnTime(), userId);
        UsersVO usersVO = usersService.findOne(userId);
        String key = RedisConfig.CacheKeys.USERS_BY_USERNAME + ":" + usersVO.getUsername();
        log.debug("tongzhuo:users:by-username={}", key);
        redisTemplate.delete(key);
        redisTemplate.delete(USERS_BY_ID + ":" + userId);

        //更新各个学习状态
        usersService.updateStatus(userId, NO_TIMING.getCode(), null, null, null);
        LearnContentDO contentDO = usersLearnService.findLearning(userId);
        contentDO.setStatus(NO_TIMING.getCode());
        usersLearnService.update(contentDO);
        UsersLearnTimingDO learnTimingDO = usersStudyService.getUsersLearnTimingByUserId(userId, YN.NO.getCode());
        learnTimingDO.setStatus(UsersLearnTimingDO.StatusEnum.valid.code);
        usersStudyService.saveUsersLearnTiming(learnTimingDO);
        return BusinessResult.createSuccessInstance(null);
    }

}
